package org.l.mybatis.core.sqlsource;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.l.mybatis.core.annotation.ParamOrder;
import org.l.mybatis.core.annotation.ParamSelect;
import org.l.mybatis.core.annotation.SqlsourceSelect;
import org.l.mybatis.core.exception.MethodDefineException;
import org.l.mybatis.core.handler.FieldHandler;
import org.l.mybatis.core.handler.ObjectHandler;

/**
 * Created by 严秋旺 on 2017/7/30.
 */
public class SqlsourceBuilderSelect extends SqlsourceBuilderAbstract implements SqlsourceBuilder {
    private final SqlCommandType sqlCommandType = SqlCommandType.SELECT;

    private SqlsourceSelect sqlsourceSelect;
    private String paramKey;
    private String orderKey;

    public SqlsourceBuilderSelect(Configuration configuration, Class<?> type, Method method,
            SqlsourceSelect sqlsourceSelect) throws MethodDefineException {
        super(configuration, type, method);
        this.sqlsourceSelect = sqlsourceSelect;
        analysis();
    }

    private void analysis() throws MethodDefineException {
        Class<?>[] paramTypes = this.method.getParameterTypes();
        Annotation[][] parameterAnnotations = this.method.getParameterAnnotations();
        this.paramCount = paramTypes.length;

        if (paramCount == 0) {
            throw new MethodDefineException("缺少表映射对象参数:" + method);
        } else if (paramCount == 1) {
        } else {
            for (int i = 0; i < paramCount; i++) {
                Class<?> paramType = paramTypes[i];

                if (findAnnotation(parameterAnnotations[i], ParamOrder.class) != null) {
                    if (String.class.isAssignableFrom(paramType)) {
                        orderKey = "param" + (i + 1);
                    } else {
                        throw new MethodDefineException("排序参数只能是字符型:" + method);
                    }

                    continue;
                }

                if (findAnnotation(parameterAnnotations[i], ParamSelect.class) != null) {
                    paramKey = "param" + (i + 1);// 后一个替代前一个
                }
            }

            if (paramKey == null) {
                throw new MethodDefineException("未指定表映射对象参数，请在对象上增加@ParamSelect:" + method);
            }
        }

    }

    @SuppressWarnings("unchecked")
    protected <T> T findAnnotation(Annotation[] parameterAnnotations, Class<? extends Annotation> T) {
        for (Annotation annotation : parameterAnnotations) {
            if (T.isAssignableFrom(annotation.annotationType())) {
                return (T) annotation;
            }
        }

        return null;
    }

    @Override
    public SqlCommandType getSqlCommandType() {
        return sqlCommandType;
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        List<ParameterMapping> parameterMappings = new ArrayList<>();
        String sql = createSql(parameterObject, parameterMappings);
        BoundSql boundSql = new BoundSql(this.configuration, sql, parameterMappings, parameterObject);
        return boundSql;
    }

    private String createSql(Object parameterObject, List<ParameterMapping> parameterMappings) {
        MetaObject metaObject;
        String paramPrefix = null;

        if (this.paramCount == 1) {
            metaObject = SystemMetaObject.forObject(parameterObject);
        } else {
            Object object = ((Map<?, ?>) parameterObject).get(this.paramKey);
            metaObject = SystemMetaObject.forObject(object);
            paramPrefix = this.paramKey;
        }

        Class<?> paramType = metaObject.getOriginalObject().getClass();
        ObjectHandler objectHandler = ObjectHandler.create(paramType);
        StringBuilder select = createSelectPart(objectHandler);
        select.append(" from ").append(objectHandler.getTableName());
        int parameterSize = parameterMappings.size();
        
        String where = createWhere(paramPrefix, parameterMappings, metaObject, objectHandler.getPrimaryFields());

        if (parameterMappings.size() <= parameterSize) {
            where = createWhere(paramPrefix, parameterMappings, metaObject, objectHandler.getDbFields());
        }
        
        select.append(where);

        if (orderKey != null) {
            String order = (String) ((Map<?, ?>) parameterObject).get(this.orderKey);

            if (StringUtils.isNotBlank(order)) {
                select.append(" order by ").append(order);
            }
        }
        
        return select.toString();
    }

    /**
     * 生成select子句
     * @param objectHandler
     * @return
     */
    private StringBuilder createSelectPart(ObjectHandler objectHandler) {
        StringBuilder select = new StringBuilder("select ");

        if (this.sqlsourceSelect.fields().length > 0) {
            int fieldSize = this.sqlsourceSelect.fields().length;

            for (int i = 0; i < fieldSize; i++) {
                if (i > 0)
                    select.append(",");

                select.append(sqlsourceSelect.fields()[i]);
            }
        } else {
            int i = 0;

            for (FieldHandler fieldHandler : objectHandler.getAllFields()) {
                if (i > 0)
                    select.append(",");

                i++;
                select.append(fieldHandler.getDbName());
            }
        }

        return select;
    }

}
